package com.beerandjesus.sportofkings.handicapper.strategy.brisnet.singlefile;

import java.math.BigDecimal;
import java.util.List;
import java.util.ArrayList;

import org.apache.commons.lang.StringUtils;

import com.beerandjesus.sportofkings.handicapper.model.*;
import com.beerandjesus.sportofkings.handicapper.model.brisnet.singlefile.*;
import com.beerandjesus.sportofkings.handicapper.factory.ProfileFactory;

public class NonMaidenRaceAnalysisStrategyImpl extends
		AbstractRaceAnalysisStrategy implements
		NonMaidenRaceAnalysisStrategy<HorseRace> {
	
	private static final int MAX_RACES_FOR_WPS_CONSIDERATION = 5;
	private static final int MAX_RACES_FOR_SPEED_CONSIDERATION = 6;
	private static final int MAX_RACES_FOR_UP_CLOSE_CONSIDERATION = 8;
    private static final BigDecimal TWO_AND_THREE_QUARTERS = new BigDecimal("2.75");
	private static final BigDecimal THREE_AND_A_HALF = new BigDecimal("3.5");

    private ProfileFactory profileFactory;

    public RaceAnalysisResultVO analyze(HorseRace race) {
        Race r = race.getRace();
        List<Horse> horses = race.getHorses();
        RaceAnalysisResultVOImpl vo = new RaceAnalysisResultVOImpl();
        setCommonFields(vo, r, horses);
        List<HorseAnalysisResult> results = new ArrayList<HorseAnalysisResult>();
        for (Horse h : horses) {
            NonMaidenHorseAnalysisResultImpl result = new NonMaidenHorseAnalysisResultImpl();
            result.setPostPosition(new Integer(h.getPostPosition()));

            // Horse name
            result.setName(h.getName());

            // Morning line odds
            result.setMorningLine(resolveMorningLineOdds(h));

            // Quirin style speed points
            Integer qPts = resolveQuirinSpeedPoints(h);
            result.setQuirinSpeedPoints(qPts.toString());

            // Result type
            ResultType type = resolveResultType(qPts);
            result.setResultType(type);

            // Speed & wps
            SpeedAndWPS speedAndWPS = resolveSpeedAndWPS(r, h);
            result.setParSpeedCount(speedAndWPS.parCount);
            result.setSpeedFigure("" + speedAndWPS.maxSpeed);
            result.setRecentWins(speedAndWPS.wins);
            result.setRecentPlaces(speedAndWPS.places);
            result.setRecentShows(speedAndWPS.shows);

            // Workouts
            result.setFastWorkouts(resolveFastWorkouts(h));

            switch (type) {
                case SLOW_EARLY_SPEED:
                    result.setUpCloseWhenStalking(resolveUpCloseWhenStalking(h));
                    break;
                case FAST_EARLY_SPEED:
                    result.setWinsOffPace(resolveWinsOffPace(h));
                    break;
            }

            // Profiles
            result.setProfiles(getProfileFactory().getProfiles(race, h));

            results.add(result);
        }

        vo.setResults(results);
        return vo;
    }

    private Boolean resolveWinsOffPace(Horse h) {
        // Note if runner can win off pace.
        // Any occurrence in past performances passes.
        Boolean winsOffPace = false;
        for (PastPerformance perf : h.getPastPerformances()) {
            if (equal(perf.getFinishPosition(), 1) && greaterThan(perf.getCallPositionSecond(), 1)) {
                winsOffPace = true;
                break;
            }
        }
        return winsOffPace;
    }

    private boolean resolveUpCloseWhenStalking(Horse h) {
        // Up close 2 times within last 8 races
        // up close =
        // 2 3/4 lengths at 2nd call for sprints
        // 3 1/2 lengths at 2nd call for routes
        int upCloseCount = 0;
        int raceCount = 0;
        for (PastPerformance perf : h.getPastPerformances()) {
            raceCount++;
            if (raceCount > MAX_RACES_FOR_UP_CLOSE_CONSIDERATION) {
                break;
            }
            String lenOrMarg = perf.getCall2ndLenOrMargin();
            if (!StringUtils.isEmpty(lenOrMarg)) {
                BigDecimal lom = new BigDecimal(lenOrMarg);
                BigDecimal comp;
                if (calc.isRoute(perf.getDistance())) {
                    comp = THREE_AND_A_HALF;
                } else {
                    comp = TWO_AND_THREE_QUARTERS;
                }
                if (lom.compareTo(comp) <= 0) {
                    upCloseCount++;
                }
            }
        }
        boolean upClose = false;
        if (upCloseCount > 1) {
            upClose = true;
        }
        return upClose;
    }

    private ResultType resolveResultType(Integer qPts) {
        ResultType type;
        if (qPts < 0) {
            type = ResultType.UNKNOWN;
        } else if (qPts < 4) {
            type = ResultType.SLOW_EARLY_SPEED;
        } else {
            type = ResultType.FAST_EARLY_SPEED;
        }
        return type;
    }

    private Integer resolveQuirinSpeedPoints(Horse h) {
        Integer qPts;
        try {
            qPts = new Integer(h.getQuirinSpeedPoints());
        } catch (NumberFormatException e) {
            qPts = -1;
        }
        return qPts;
    }

    private SpeedAndWPS resolveSpeedAndWPS(Race r, Horse h) {
        SpeedAndWPS speedAndWPS = new SpeedAndWPS();
        int raceCount = 0;
        for (PastPerformance perf : h.getPastPerformances()) {
            raceCount++;
            if (raceCount <= MAX_RACES_FOR_SPEED_CONSIDERATION) {
                Integer speed = getInteger(perf.getBrisSpeedRating(), 0);
                if (speed > speedAndWPS.maxSpeed) {
                    speedAndWPS.maxSpeed = speed;
                }
                if (speed >= getInteger(r.getBrisSpeedPar(), 0)) {
                    speedAndWPS.parCount++;
                }
            }
            if (raceCount <= MAX_RACES_FOR_WPS_CONSIDERATION) {
                Integer pos = getInteger(perf.getFinishPosition(), 99);
                if (pos == 1) {
                    speedAndWPS.wins++;
                } else if (pos == 2) {
                    speedAndWPS.places++;
                } else if (pos == 3) {
                    speedAndWPS.shows++;
                }
            }

        }
        return speedAndWPS;
    }

    public ProfileFactory getProfileFactory() {
        return profileFactory;
    }

    public void setProfileFactory(ProfileFactory profileFactory) {
        this.profileFactory = profileFactory;
    }

    private final class SpeedAndWPS {
        protected int maxSpeed = 0;
        protected int parCount = 0;
        protected int wins = 0;
        protected int places = 0;
        protected int shows = 0;

    }

}
